//#define _GNU_SOURCE

#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>

#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <linux/fs.h>

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/select.h>

#include <libaio.h>

#include "bmap.h"

#define BMAP_LINEBUF 40960  //please make sure this VALUE big enough...


struct bmap *bmap_create(const char *filename, long blksize)
{
	assert(filename!=NULL);
	struct bmap *p = (struct bmap *)malloc(sizeof(struct bmap));
	assert(p!=NULL);
	p->filename = strcpy((char *)malloc(strlen(filename)+1), filename);
	p->blksize = blksize;
	p->blkcnt = 0;
	p->head = NULL;
	p->tail = NULL;
	return p;
}
void bmap_destroy(struct bmap *p)
{
	assert(p!=NULL);
	while(p->head){
		struct bmap_extent *e = p->head;
		p->head = e->next;
		free(e);
	}
	free(p->filename);
	free(p);
}

void bmap_append(struct bmap *p, uint64_t offset, uint64_t length)
{
	assert(p!=NULL);
	p->blkcnt += length;
	if((p->tail!=NULL)&&(p->tail->offset + p->tail->length == offset)){
		p->tail->length += length;
		return;
	}
	struct bmap_extent *e = (struct bmap_extent *)malloc(sizeof(struct bmap_extent));
	assert(e!=NULL);
	e->offset = offset;
	e->length = length;
	e->next = NULL;
	
	//add the extent to the FileBmap:
	if(p->tail==NULL){
		p->head = e;
	}else{
		p->tail->next = e;
	}
	p->tail = e;
}



///////////////////////////////////////////////////////////////////////////////
void bmap_show(struct bmap *p)
{
	struct bmap_extent *e;
	assert(p!=NULL);
	printf("%s ->", p->filename);
	for(e=p->head;e!=NULL; e=e->next){
		printf(" %llu(+%llu)", (unsigned long long)e->offset, (unsigned long long)e->length);
	}
	printf("\n");
}



struct bmap * bmap_load(FILE *fp)
{
	struct bmap *bm;
	char *filename, *extent;
	long blksize;
	char buffer[BMAP_LINEBUF];
	assert(fp!=NULL);
	if(!fgets(buffer, BMAP_LINEBUF, fp)){
		// error? or EOF?
		if(!feof(fp)){
			// ERROR
			perror("bmap_load failed:");
			exit(-1);
		}else{
			// EOF
			return NULL;
		}
	}
	//resovle the buffer:
	filename = strtok(buffer, ":");
	blksize = atol(strtok(NULL, ":"));
	bm = bmap_create(filename, blksize);
	while((extent=strtok(NULL, ", "))!=NULL){
		unsigned long long offset, length;
		if(sscanf(extent, "%llu(+%llu)", &offset, &length)!=2){
			fprintf(stderr, "ERROR: bmap_load sscanf failed!\n");
			exit(-1);
		}
		bmap_append(bm, offset, length);
	}
	return bm;
}

void bmap_save(struct bmap *bm, FILE *fp)
{
	struct bmap_extent *e;
	char line[BMAP_LINEBUF], *buffer=line;

	assert(bm!=NULL);
	assert(fp!=NULL);
	//check if ZERO-sized file:
	if(bm->blkcnt==0){
		return;
	}
	buffer += sprintf(buffer, "%s: %ld:", bm->filename, bm->blksize);
	for(e=bm->head;e!=NULL; e=e->next){
		buffer += sprintf(buffer, " %llu(+%llu)", (unsigned long long)e->offset, (unsigned long long)e->length);
		if((buffer-line) > BMAP_LINEBUF){
			fprintf(stderr, "ERROR: bmap_save buffer is overflow, make it bigger!!\n");
			exit(-1);
		}
	}
	buffer += sprintf(buffer, "\n");
	if(fputs(line, fp)<0){
		perror("bmap_save failed:");
		exit(-1);
	}
}


